MERN Stack
#
Lesson Objectives- Describe the elements of the MERN stack
- Set up static files
- Set up React using Create React App
- Get Data from our holidays_api app
- Do rest of CRUD
#
Describe the elements of the MERN stackMERN stack is just a collection of tech that work well together
- Mongo
- Express
- React
- Node.js
We're going to have two apps: One will be our create-react-app that will handle the UX/UI
The other is our backend express API.
It is increasingly common to have a separate back end that just handles data: Desktop and mobile apps are different apps for a user to use, but it makes sense they'd share the backend/data rather than building out that portion twice.
We're going to make a top level folder that will hold both of our apps
#
Create React Appcd holidays_front_end
npm install
npm start
What we're building
#
Get Data from Our APIFirst, we'll need to store our data in the state of our App.
import { useState } from "react";
const App = () => { const [holidays, setHoliday] = useState([]);
return ( <div className="container"> <h1>Holidays! Celebrate!</h1> </div> );};
export default App;
We can use fetch
to make database calls for us
fetch("/holidays") .then( (data) => data.json(), (err) => console.log(err) ) .then( (parsedData) => console.log(parsedData), (err) => console.log(err) );
We will likely receive a CORS ERROR
For safety reasons, requests with different origins are blocked by default.
We'll have to move over to our backend and add an npm package called cors
-npm install cors
- in
server.js
, with your other dependencies
const cors = require("cors");
- further down, with your other middleware
Note: we only need localhost for this build, but to give you an example of how to limit CORS to a white list for your project (ie your localhost and then your api on heroku)
server.js (express backend)
const whitelist = [ "http://localhost:3000", "https://fathomless-sierra-68956.herokuapp.com",];const corsOptions = { origin: (origin, callback) => { if (whitelist.indexOf(origin) !== -1) { callback(null, true); } else { callback(new Error("Not allowed by CORS")); } },};
// all routes are now exposed, // sometimes you just want to limit access // (ie OMDB - it's ok for anyone to see the movies, // but you don't want just anyone updating the movies)app.use(cors(corsOptions));
Now in your console you should be able to see your holiday coming back from your express backend api!
#
Show A list of HolidaysRight now we're just calling this fetch on loading of this react app. But we're going to want to be able to call this functionality again and again.
Inside the class App, let's put this code inside a useEffect
. We'll also set up state to hold our array of holidays
import { useEffect, useState } from "react";
const App = () => { const [holidays, setHolidays] = useState([]); useEffect(() => { fetch("/holidays") .then( (data) => data.json(), (err) => console.log(err) ) .then( (parsedData) => setHolidays(parsedData), (err) => console.log(err) ); }, []);
return ( <div className="container"> <h1>Holidays! Celebrate!</h1> </div> );};
useEffect
#
When to call We want to call useEffect
when we render the app.
If we put it in the render function, we'll create an infinite loop. If we call it in the constructor it might be alright. Since JS is asynchronous sometimes that fetch request will complete at the right time, sometimes it won't.
We need a more surefire way to call this function at the right time. React has some lifecycle
methods for exactly this purpose. We may want to run things when a component is mounted on the DOM, when a component is unmounted, when it is updated and more. We will use useEffect(() => {...}, [])
for our purpose
We should still see our holiday(s) from our express API console logging.
Now we want to set that data in state
Finally, we want to render it:
<h1>Holidays! Celebrate!</h1><table> <tbody> {holidays.map((holiday) => { return ( <tr key={holiday._id}> <td> {holiday.name}</td> </tr> ); })} </tbody></table>
Full Code: App.js
import { useEffect, useState } from "react";
const App = () => { const [holidays, setHolidays] = useState([]);
useEffect(() => { fetch("/holidays") .then( (data) => data.json(), (err) => console.log(err) ) .then( (parsedData) => setHolidays(parsedData), (err) => console.log(err) ); }, []);
return ( <div className="container"> <h1>Holidays! Celebrate!</h1> <table> <tbody> {holidays.map((holiday) => { return ( <tr key={holiday._id}> <td> {holiday.name}</td> </tr> ); })} </tbody> </table> </div> );};